﻿using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace NodeBasedSkillEditor.ViewModel
{
    public class VM_NodeLibView : ObservableObject
    {
        private string _filter = "";
        public string Filter {
            get { return _filter; }
            set {
                _filter = value;
                FilterLower = _filter.ToLower();
                OnPropertyChanged();
                OnFilterChanged();
            }
        }
        public string FilterLower { get; private set; } = "";

        private ObservableCollection<string> _displayNodeCategories = new();
        public ObservableCollection<string> DisplayNodeCategories {
            get { return _displayNodeCategories; }
            set { _displayNodeCategories = value; OnPropertyChanged(); }
        }

        private object _selectedCategory;
        public object SelectedCategory {
            get { return _selectedCategory; }
            set {
                _selectedCategory = value;
                OnPropertyChanged();
                OnSelectedCategoryChanged(_selectedCategory);
            }
        }

        private object _selectedNodeItem;
        public object SelectedNodeItem {
            get { return _selectedNodeItem; }
            set { _selectedNodeItem = value; OnPropertyChanged(); }
        }


        private ObservableCollection<VM_NodeListBoxItem> _displayNodesInCurrCate = new();
        public ObservableCollection<VM_NodeListBoxItem> DisplayNodesInCurrCate {
            get { return _displayNodesInCurrCate; }
            set { _displayNodesInCurrCate = value; OnPropertyChanged(); }
        }

        public RelayCommand<MouseButtonEventArgs> OnPreviewLMBDownOnNodeItemCmd { get; set; }

        private string _currCategory;

        private static Dictionary<string, List<VM_NodeListBoxItem>> _originAllNodeItemDictionary;

        private static Dictionary<string, List<VM_NodeListBoxItem>> _allNodeItemDictionary {
            get {
                if (_originAllNodeItemDictionary == null)
                {
                    _originAllNodeItemDictionary = new Dictionary<string, List<VM_NodeListBoxItem>>();
                    foreach (var c in DAGHelper.AllNodeTypeDictionary)
                    {
                        List<VM_NodeListBoxItem> currNodeItemList = new();
                        foreach (var n in c.Value)
                        {
                            var currNodeName = n.Key;
                            var currNodeType = n.Value;
                            var attr = currNodeType.GetCustomAttribute<NodeAttribute>();
                            var currNodeItem = new VM_NodeListBoxItem() {
                                NodeName = n.Key,
                                NodeType = n.Value,
                                SearchTag = attr.SearchTag
                            };
                            currNodeItemList.Add(currNodeItem);
                        }

                        _originAllNodeItemDictionary.Add(c.Key, currNodeItemList);
                    }
                }
                return _originAllNodeItemDictionary;
            }
        }

        public VM_NodeLibView()
        {
            OnPreviewLMBDownOnNodeItemCmd = new RelayCommand<MouseButtonEventArgs>(OnStartDragNodeInLib);

            string firstCate = null;
            foreach (var c in _allNodeItemDictionary)
            {
                if (firstCate == null)
                {
                    firstCate = c.Key;
                }
                DisplayNodeCategories.Add(c.Key);
            }
            SelectedCategory = firstCate;
        }

        void OnFilterChanged()
        {
            var prevCate = _currCategory;
            DisplayNodeCategories.Clear();
            foreach (var c in _originAllNodeItemDictionary)
            {
                var hasNodePassFilter = false;
                foreach (var nt in c.Value)
                {
                    if ((!string.IsNullOrEmpty(nt.NodeName) && nt.NodeName.ToLower().Contains(FilterLower))
                        || (!string.IsNullOrEmpty(nt.SearchTag) && nt.SearchTag.Contains(Filter)))
                    {
                        hasNodePassFilter = true;
                        break;
                    }
                }
                if (hasNodePassFilter)
                {
                    DisplayNodeCategories.Add(c.Key);
                }
            }
            SelectedCategory = DisplayNodeCategories.Count > 0 ? DisplayNodeCategories[0] : "";
            RefreshVisibleNodesInCategory(_currCategory);
        }

        void OnSelectedCategoryChanged(object newSel)
        {
            _currCategory = newSel as string;
            RefreshVisibleNodesInCategory(_currCategory);
        }

        void RefreshVisibleNodesInCategory(string category)
        {
            DisplayNodesInCurrCate.Clear();
            if (string.IsNullOrEmpty(category))
            {
                return;
            }

            List<VM_NodeListBoxItem> nodes;
            if (_originAllNodeItemDictionary.TryGetValue(category, out nodes))
            {
                foreach (var n in nodes)
                {
                    var lowerNodeName = n.NodeName.ToLower();
                    var searchTag = n.SearchTag;
                    if (lowerNodeName.Contains(FilterLower) 
                        || (!string.IsNullOrEmpty(searchTag) && searchTag.Contains(Filter)))
                    {
                        DisplayNodesInCurrCate.Add(n);
                    }
                }
            }
        }

        public void OnStartDragNodeInLib(MouseButtonEventArgs e)
        {
            var elem = e.OriginalSource as FrameworkElement;
            var nodeItem = elem.DataContext as VM_NodeListBoxItem;

            if (nodeItem != null)
            {
                DataObject data = new DataObject();

                //data.SetData(DataFormats.StringFormat, nodeItem.NodeName);
                data.SetData("NodeType", nodeItem.NodeType);

                DragDrop.DoDragDrop(e.Source as DependencyObject, data, DragDropEffects.Copy);
            }
        }
    }
}
